/*
 * Copyright (C) 2011 Emmanuel Tourdot
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 *
 * $Id$
 */
package org.etourdot.xinclude;

import com.google.common.base.Strings;
import net.sf.saxon.s9api.Processor;
import org.xml.sax.helpers.NamespaceSupport;

import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.Stack;

/**
 * @author Emmanuel Tourdot
 */
public class XIncProcEngine implements XIncProcEngineHandler
{
    public static final String XINCLUDE_NAMESPACE_URI = "http://www.w3.org/2001/XInclude";
    public static final QName XINCLUDE_QNAME = new QName(XINCLUDE_NAMESPACE_URI, "include", "xi");
    public static final QName FALLBACK_QNAME = new QName(XINCLUDE_NAMESPACE_URI, "fallback", "xi");
    public static final QName XMLBASE_QNAME = new QName(NamespaceSupport.XMLNS, "base", "xml");

    private XIncProcConfiguration configuration;

    public XIncProcEngine()
    {
        this.configuration = new XIncProcConfiguration();
    }

    public XIncProcEngine(final Processor processor)
    {
        this.configuration = new XIncProcConfiguration(processor);
    }

    public XIncProcEngine(final XIncProcConfiguration configuration)
    {
        this.configuration = configuration;
    }

    @Override
    public void parse(final URI baseURI, final OutputStream output) throws XIncludeException
    {
        configuration.getEngine().parse(baseURI, output);
    }

    @Override
    public void parse(final Source source, final Result result) throws XIncludeException
    {
        configuration.getEngine().parse(source, result);
    }

    @Override
    public void parse(final InputStream source, final String systemId, final OutputStream result) throws XIncludeException
    {
        configuration.getEngine().parse(source, systemId, result);
    }

    public XIncProcConfiguration getConfiguration()
    {
        return configuration;
    }

    public void setConfiguration(final XIncProcConfiguration configuration)
    {
        this.configuration = configuration;
    }

    public static boolean isXInclude(final QName qname)
    {
        if (XINCLUDE_QNAME.getLocalPart().equals(qname.getLocalPart()))
        {
            if (Strings.isNullOrEmpty(qname.getNamespaceURI()) ||
                XINCLUDE_QNAME.getNamespaceURI().equals(qname.getNamespaceURI()))
            {
                return true;
            }
        }
        return false;
    }

    public static boolean isFallback(final QName qname)
    {
        if (FALLBACK_QNAME.getLocalPart().equals(qname.getLocalPart()))
        {
            if (Strings.isNullOrEmpty(qname.getNamespaceURI()) ||
                FALLBACK_QNAME.getNamespaceURI().equals(qname.getNamespaceURI()))
            {
                return true;
            }
        }
        return false;
    }

    public static boolean checkVal(final String val)
    {
        final byte[] bytes = val.getBytes();
        for (byte aByte : bytes)
        {
            if (aByte < 32 || aByte > 126)
            {
                return true;
            }
        }
        return false;
    }
    
    public static URI getBase(final Stack stack, final URI currentURI, final URI baseURI) throws XIncludeException
    {
        if (currentURI.isAbsolute())
        {
            return currentURI;
        }
        else
        {
            final URI prevBase;
            if (stack.empty())
            {
                prevBase = baseURI;
            }
            else
            {
                prevBase = (URI) stack.pop();
            }
            final URI resolvedUri = prevBase.resolve(currentURI);
            if (resolvedUri.equals(prevBase))
            {
                throw new XIncludeException("Inclusion loop error");
            }
            return getBase(stack, resolvedUri, baseURI);
        }
    }
}
